#include "IRCtrol.h"
#include "interface.h"

// extern unsigned char ir_rec_flag; // 接收数据标志位 1 有新数据 0 没有
unsigned char IRCOM[4];

// using time3 to realize delay systick
// time2 used in common constant-time tasks, systick used for common delay
// tasks, thus cannot be reused for such interrupt.
void Time3Init(void) {
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
  TIM_TimeBaseStructure.TIM_Prescaler = (72 - 1); // 72M -> 1MHz
  TIM_TimeBaseStructure.TIM_Period = (2 - 1);     // 2us
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Down;
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
}

// 1us 延时
void DelayUs(vu32 nCount) {
  u16 TIMCounter = nCount;
  TIM_Cmd(TIM3, ENABLE);
  TIM_SetCounter(TIM3, TIMCounter);
  while (TIMCounter > 1) {
    TIMCounter = TIM_GetCounter(TIM3);
  }
  TIM_Cmd(TIM3, DISABLE);
}

// 外部中断配置 红外遥控配置
void IRCtrolInit(void) {
  GPIO_InitTypeDef GPIO_InitStructure;
  EXTI_InitTypeDef EXTI_InitStructure; // 定义一个外部中断相关的结构体
  NVIC_InitTypeDef NVIC_InitStructure; // 定义一个中断的结构体

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
  GPIO_InitStructure.GPIO_Pin = IRIN_PIN;       // 配置使能GPIO管脚
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 配置GPIO模式，输入上拉
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 配置GPIO端口速度
  GPIO_Init(IRIN_GPIO, &GPIO_InitStructure);

  GPIO_EXTILineConfig(IRIN_PORTSOURCE, IRIN_PINSOURCE);
  EXTI_InitStructure.EXTI_Line = IRIN_EXITLINE; // 将对应的GPIO口连接到中断线上
  EXTI_InitStructure.EXTI_Trigger =
      EXTI_Trigger_Falling; // 中断事件类型，下降沿
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 选择模式，中断型
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;           // 使能该中断
  EXTI_Init(&EXTI_InitStructure); // 将配置好的参数写入寄存器

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);  // 阶级为0，不可嵌套
  NVIC_InitStructure.NVIC_IRQChannel = IRIN_IRQCH; // 打开PINA_8的外部中断
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 主优先级0，最高
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级，最低
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;    // 使能该模块中断
  NVIC_Init(&NVIC_InitStructure); // 中断初始化，将结构体定义的数据执行

  Time3Init();
}

/**
 * @brief Delays for a specified number of iterations.
 *
 * This function introduces a delay by calling DelayUs with a fixed
 * delay of 140 microseconds in a loop. The loop runs for the number
 * of iterations specified by the parameter x.
 *
 * @param x The number of delay iterations to perform.
 */
void DelayIr(unsigned char x) {
  while (x--) {
    DelayUs(140);
  }
}

/**
 * @brief   IR external interrupt service routine
 * @details This function is to collect IR remote control data and convert it to
 *          a command for the car.
 * @note    This function is called by the external interrupt handler.
 * @note    The IR remote control data is collected by counting the time
 * duration of the IR signal. The data is collected in 4 bytes, each byte has 8
 *          bits. The high bit of each byte is the start bit, and the low bit is
 *          the stop bit. The data is collected from the IR signal by counting
 *          the time duration of the IR signal. The time duration is calculated
 *          by using the timer 3 to count the number of clock cycles. The number
 *          of clock cycles is calculated by using the DelayIr function.
 * @note    The command is converted from the IR remote control data by using a
 *          switch statement. The command is stored in the ctrl_comm variable.
 * @note    The ir_rec_flag is set to 1 if the command is valid.
 */

#define MERGED_IRCTROL
#ifndef MERGED_IRCTROL
void IRIntIsr(void) {
  unsigned char j, k, N = 0;
  DelayIr(15);
  if (IRIN == 1) {
    return;
  }
  continue_time = 40; // 连发信号，表示指令持续 40*5 = 200ms 无指令停车
                      // 确认IR信号出现
  while (!IRIN) {
    DelayIr(1); // 等IR变为高电平，跳过9ms的前导低电平信号。
  }
  // 收集四组数据，每组数据有8位
  for (j = 0; j < 4; j++) {
    for (k = 0; k < 8; k++) {
      while (IRIN) {
        DelayIr(1); // 等 IR 变为低电平，跳过4.5ms的前导高电平信号。
      }
      while (!IRIN) {
        DelayIr(1); // 等 IR 变为高电平
      }
      while (IRIN) {
        DelayIr(1); // 计算IR高电平时长
        N++;
        if (N >= 30) {
          return; // 0.14ms计数过长自动离开
        }
      } // 高电平计数完毕
      IRCOM[j] = IRCOM[j] >> 1; // 数据最高位补“0”
      if (N >= 8) {
        IRCOM[j] = IRCOM[j] | 0x80; // 数据最高位补“1”
      }
      N = 0;
    } // end for k
  } // end for j

  k = ~IRCOM[3];
  if (IRCOM[2] != k) {
    return;
  }

  // 指令转换
  switch (IRCOM[2]) {
  case 0x46:
    ctrl_comm = COMM_UP;
    break;
  case 0x15:
    ctrl_comm = COMM_DOWN;
    break;
  case 0x44:
    ctrl_comm = COMM_LEFT;
    break;
  case 0x43:
    ctrl_comm = COMM_RIGHT;
    break;
  case 0x40:
    ctrl_comm = COMM_STOP;
    break;
  default:
    return;
  }
  ir_rec_flag = 1;
}
#else
void IRIntIsr(void) {
  unsigned char j, k, N = 0;
  DelayIr(15);
  if (IRIN == 1) {
    return;
  }
  continue_time = 40; // 连发信号，表示指令持续 40*5 = 200ms 无指令停车
  control_time = 400;
  // 确认IR信号出现
  while (!IRIN) // 等IR变为高电平，跳过9ms的前导低电平信号。
  {
    DelayIr(1);
  }

  for (j = 0; j < 4; j++) // 收集四组数据
  {
    for (k = 0; k < 8; k++) // 每组数据有8位
    {
      while (IRIN) // 等 IR 变为低电平，跳过4.5ms的前导高电平信号。
      {
        DelayIr(1);
      }
      while (!IRIN) // 等 IR 变为高电平
      {
        DelayIr(1);
      }
      while (IRIN) // 计算IR高电平时长
      {
        DelayIr(1);
        N++;
        if (N >= 30) {
          return;
        } // 0.14ms计数过长自动离开。
      } // 高电平计数完毕
      IRCOM[j] = IRCOM[j] >> 1; // 数据最高位补“0”
      if (N >= 8) {
        IRCOM[j] = IRCOM[j] | 0x80;
      } // 数据最高位补“1”
      N = 0;
    } // end for k
  } // end for j

  k = ~IRCOM[3];
  if (IRCOM[2] != k) {
    return;
  }

  // 指令转换
  ctrl_comm_last = ctrl_comm;
  switch (IRCOM[2]) {
  case 0x46:
    ctrl_comm = COMM_UP;
    break;
  case 0x15:
    ctrl_comm = COMM_DOWN;
    break;
  case 0x44:
    ctrl_comm = COMM_LEFT;
    break;
  case 0x43:
    ctrl_comm = COMM_RIGHT;
    break;
  case 0x40:
    ctrl_comm = COMM_STOP;
    break;
  default:
    ctrl_comm = COMM_DOWN;
    return;
  }
  ir_rec_flag = 1;
}
#endif

static int initPeriodTimer = 0, easyStep = 0, byteSerial = 0, charSerial = 0;
const int loopPeriod = 7;
static int littleTimer = 0;
static uint32_t value = 0;

void byteReaderTick(void) {
  littleTimer++;
  if (littleTimer >= loopPeriod) {
    littleTimer = 0;
  }
}
static inline int readByteFromIR(void) {
  if (littleTimer == 0) {
    value = IRIN;
    return 1;
  }
  return 0;
}
static inline uint32_t getByteReaderValue(void) { return value; }
static int thisStep = 0, thisTimer = 0;
unsigned char highTime = 0;
int mainHighCounter(void) {
  thisTimer++;
  if (thisStep == 0) { // 等 IR 变为低电平，跳过4.5ms的前导高电平信号。
    if (readByteFromIR()) {
      if (getByteReaderValue()) {
        return 0;
      } else {
        thisStep = 1;
        return 0;
      }
    }
  } else if (thisStep == 1) { // 等 IR 变为高电平
    if (readByteFromIR()) {
      if (!getByteReaderValue()) {
        return 0;
      } else {
        thisStep = 2;
        return 0;
      }
    }
  } else if (thisStep == 2) { // 计算IR高电平时长
    if (readByteFromIR()) {
      if (getByteReaderValue()) {
        highTime++;
        if (highTime >= 30) { // 0.14ms计数过长自动离开。
          return 1;
        }
      } else {
        thisStep = 3;
        return 0;
      }
    }
  } else if (thisStep == 3) { // 高电平计数完毕
    thisStep = 0;
    return 1;
  }
  return 0;
}
static inline unsigned char getHighTimeCount(void) {
  unsigned char tmpVar = highTime;
  highTime = 0;
  return tmpVar;
}

static unsigned char customIRComm[4] = {0};
void IRTimerHandler(void) {
  byteReaderTick();
  if (easyStep == 0) { // 初始等待15*0.14ms
    initPeriodTimer++;
    if (initPeriodTimer < 15) {
      return;
    } else {
      easyStep = 1;
      initPeriodTimer = 0;
      return;
    }
  } else if (easyStep == 1) { // 仍为高电平则认为信号错误，跳过
    if (readByteFromIR()) {
      if (getByteReaderValue()) {
        easyStep = 0;
        return;
      } else {
        easyStep = 2;
        return;
      }
    }
  } else if (easyStep == 2) { // 等待IR信号再次出现，确认有指令发出
    if (readByteFromIR()) {
      if (!getByteReaderValue()) {
        return;
      } else {
        easyStep = 3;
        byteSerial = 0;
        charSerial = 0;
        return;
      }
    }
  } else if (easyStep == 3) { // 已经确认有信号出现，进入数据接收阶段
    if (mainHighCounter()) {
      customIRComm[charSerial] = customIRComm[charSerial] >> 1;
      if (getHighTimeCount() >= 8) {
        customIRComm[charSerial] = customIRComm[charSerial] | 0x80;
      }
      byteSerial++;
      if (byteSerial >= 8) {
        byteSerial = 0;
        charSerial++;
      }
      if (charSerial >= 4) {
        charSerial = 0;
        easyStep = 4;
        return;
      }
    }
  } else if (easyStep == 4) {
    byteSerial = ~customIRComm[3];
    if (customIRComm[2] != byteSerial) {
      easyStep = 0;
      return;
    } else {
      easyStep = 5;
      return;
    }
  } else if (easyStep == 5) {
    switch (customIRComm[2]) {
    case 0x46:
      ctrl_comm = COMM_UP;
      break;
    case 0x15:
      ctrl_comm = COMM_DOWN;
      break;
    case 0x44:
      ctrl_comm = COMM_LEFT;
      break;
    case 0x43:
      ctrl_comm = COMM_RIGHT;
      break;
    case 0x40:
      ctrl_comm = COMM_STOP;
      break;
    default:
      ctrl_comm = COMM_DOWN;
      return;
    }
    ir_rec_flag = 1;
    easyStep = 0;
    return;
  }
}
